package com.javaweb.aop;

import ch.qos.logback.core.net.SocketConnector;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
/**
 * @desc:
 * @author:
 * @date:
 */

/**
 * TODO:
 * 1. 对所有Dao中select方法进行日志输出，日志需要包含 函数名字
 * 2. 对所有Dao中select方法进行执行时间日志输出，日志需要包含 函数名字及执行时间。
 *
 *
 */

@Aspect
@Component
@Slf4j
public class LogAspect {

    /**
     * point cut
     */
    @Pointcut("execution(public * com.javaweb.dao..*.*(..))")
    public void daoLayerAllMethods(){} // 用一个空方法当 point Cut变量

    @Pointcut("execution(* com.javaweb.dao..*.insert*(..))")
    public void daoLayerMethodsStartWithInsert(){}

    @Pointcut("@within(com.javaweb.aop.ExceptionHandler) || @annotation(com.javaweb.aop.ExceptionHandler)")
    public void exceptionHandlerPointcut() {}
    /**
     * advices
     */
    @Pointcut("execution(* com.javaweb.dao..*.select*(..))")
    public void daoLayerSelectMethods() {}

    /**
     * Before advice for select methods in DAOs
     */
    @Before("daoLayerSelectMethods()")
    public void logBeforeSelectMethod(JoinPoint jp) {
        log.info("Select method: " + jp.getSignature().getName() + " is about to execute.");
    }
    @Before("daoLayerAllMethods()")
    public void simpleLogBeforeMethod(JoinPoint jp) {

        log.info("method: "+ jp.getSignature().getName()+" was ready for execution.") ;

    }

    @After("daoLayerMethodsStartWithInsert()")
    public void afterAllInsertMethods(JoinPoint jp) {
        log.info("method: " + jp.getSignature().getName() + " was invoked no matter" +
                " whether its calling was normal nor not.") ;
    }

    @AfterThrowing(pointcut = "exceptionHandlerPointcut()", throwing = "ex")
    public void handleException(JoinPoint joinPoint, Throwable ex) {
        Signature signature = joinPoint.getSignature();
        String methodName = joinPoint.getSignature().getName();
        System.out.println("Method: " + methodName + " threw an exception: " + ex.getMessage());
    }

    @Around("daoLayerAllMethods()")
    public Object measurePerformanceForAllDaoMethods(
            ProceedingJoinPoint pjp) {
        /**
         * before method execution, set a starting time.
         */
        Long start = System.currentTimeMillis() ;
        String methodName = pjp.getSignature().getName() ;
        Object ret = null ;
        try {
            ret = pjp.proceed();
        } catch (Throwable e) {
            log.error("method: " + methodName
                    + " met an error: " + e.getMessage()) ;
        }
        /**
         * after its execution, set an ending time-point.
         */
        Long end = System.currentTimeMillis() ;
        /*
          log for performance.
         */
        log.info("method: " + methodName
                + ", spent (s): " + (end - start) / 1000.0) ;
        return ret ;


    }
    @Around("daoLayerSelectMethods()")
    public Object logAndMeasureSelectMethodExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
        Long start = System.currentTimeMillis();
        String methodName = pjp.getSignature().getName();

        Object result = null;
        try {
            result = pjp.proceed();
        } catch (Throwable e) {
            log.error("Select method: " + methodName + " encountered an error: " + e.getMessage());
            throw e; // Re-throw the exception to let it propagate
        }

        Long end = System.currentTimeMillis();
        log.info("Select method: " + methodName + " executed in " + (end - start) + " milliseconds.");

        return result;
    }

}